#include "htil.hpp"
#include "./cppjieba/Jieba.hpp"

// 用于倒排的DocNode
typedef struct DocNode
{
    uint64_t doc_id_;       // 出自哪个文档
    int weight_;            // 权重
    std::string word_;      // 词

}DocNode;

typedef std::vector <DocNode> InvertedList;

typedef struct WordCnt
{
public:
    int content_cnt_;
    int title_cnt_;
    WordCnt()
    {
        content_cnt_ = 0;
        title_cnt_ = 0;
    }
}WordCnt;


typedef struct ResultNode
{
    uint64_t id_;
    int weight_;
    std::string title_;
    std::string url_;
    std::string summary_;
}ResultNode;

typedef std::vector<ResultNode> ResultList;

class Index
{
public:
    Logging* log_point_;
private:
    std::vector<DocInfo> forward_index_;
    std::unordered_map<std::string, InvertedList> inverted_index_;
    cppjieba::Jieba* jieba_;

public:
    Index() :jieba_(nullptr) {}
    ~Index()
    {
        if(jieba_ != nullptr)
            delete jieba_;
    }
    void init(const std::string dict_path, const std::string hmm_model_path,
        const std::string user_dict_path, const std::string idf_dict_path,
        const std::string stop_word_path, Logging* log_point_P)
    {
        jieba_ = new cppjieba::Jieba(dict_path, hmm_model_path, user_dict_path,
            idf_dict_path, stop_word_path);
        log_point_ = log_point_P;
    }
    // 构建正排
    bool BuildForwardIndex(const std::string& out_path)
    {
        std::ifstream in(out_path.c_str());
        if(!in.is_open())
        {
            std::cout << "[ERROR] 文件打开失败, 请检查路径 " << common::GetTime() << std::endl;
            log_point_->WriteLogToDoc("ERROR", "文件打开失败, 请检查路径 ", common::GetTime());
            return false;
        }
        std::string line;
        int i = 0;
        while(std::getline(in, line))
        {
            DocInfo doc;
            common::SplitLineToDoc(&doc, line, "\3");
            doc.docId_ = forward_index_.size() + 1;
            forward_index_.push_back(doc);
            if(i % 100 == 0)
            {
                std::cout << "[DEBUG] 构建正排索引: " << i << " " << common::GetTime() << std::endl;
                log_point_->WriteLogToDoc("DEBUG", "构建正排索引: " + std::to_string(i), common::GetTime());
            }
            ++i;
        }
        return true;
    }
    bool GetDocInfo(const uint64_t& doc_id, DocInfo* dest)
    {
        if(doc_id >= forward_index_.size())
        {
            return false;
        }
        *dest = forward_index_[doc_id];
        return true;
    }
    bool GetInvertedList(const std::string& key, InvertedList* dest)
    {
        auto result = inverted_index_.find(key);
        if(result == inverted_index_.end())
        {
            return false;
        }
        *dest = result->second;
        return true;
    }

    // 构建倒排
    bool BuildInvertedIndex()
    {
        uint64_t i = 0;
        // 对正排遍历 对标题和内容分词 对分词得到的结果加一
        for(DocInfo& doc_info : forward_index_)
        {
            
            // 分词标题
            std::unordered_map <std::string, WordCnt> word_cnt_map;
            std::vector<std::string> title_token;
            // 把docTitle分词 遍历分词结果 去++数量
            CountWord(doc_info.docTitle_, &title_token);
            for(std::string& title : title_token)
            {
                boost::to_lower(title);
                ++word_cnt_map[title].title_cnt_;
            }
            std::vector<std::string> content_token;
            // 把docContent分词 遍历分词结果 去++数量
            CountWord(doc_info.docContent_, &content_token);
            for(std::string& content : content_token)
            {
                boost::to_lower(content);
                ++word_cnt_map[content].content_cnt_;
            }

            for(const auto& word_cnt : word_cnt_map)
            {
                DocNode docNode;
                docNode.doc_id_ = doc_info.docId_;
                docNode.word_ = word_cnt.first;
                docNode.weight_ = word_cnt.second.title_cnt_ * 10 + word_cnt.second.content_cnt_;
                inverted_index_[docNode.word_].push_back(docNode);
            }
            if(i % 100 == 0)
            {
                std::cout << "[DEBUG] 构建倒排拉链: " << i << " " << common::GetTime() << std::endl;
                log_point_->WriteLogToDoc("DEBUG", "构建倒排拉链: " + std::to_string(i), common::GetTime());
            }
            ++i;
        }
        std::cout << "[SUCCESS] 构建完毕" << common::GetTime() << std::endl;
        log_point_->WriteLogToDoc("SUCCESS", "构建完毕", common::GetTime());
        return true;

    }
    void CountWord(const std::string& str, std::vector<std::string>* tokens)
    {
        jieba_->CutForSearch(str, *tokens);
    }
private:
    // 防止拷贝, Index 是一个很大的对象, 拷贝的成本很大.
    Index(const Index&) = delete;
    Index& operator=(const Index&) = delete;
};


class Search
{
private:
    Index index_;
    Logging* log_point_;
public:
    Search(){}
    bool build(const std::string out_path)
    {

        if(!index_.BuildForwardIndex(out_path))
        {
            std::cout << "[ERROR] 正排构建失败" << common::GetTime() << std::endl;
            log_point_->WriteLogToDoc("ERROR", "正排构建失败", common::GetTime());
            return false;
        }
        std::cout << "[SUCCESS] 正排序完毕" << common::GetTime() << std::endl;
        log_point_->WriteLogToDoc("SUCCESS", " 正排序完毕", common::GetTime());
        if(!index_.BuildInvertedIndex())
        {
            std::cout << "[ERROR] 倒排构建失败" << common::GetTime() << std::endl;
            log_point_->WriteLogToDoc("ERROR", "倒排构建失败", common::GetTime());
            return false;
        }
        std::cout << "[SUCCESS] 倒排序完毕" << common::GetTime() << std::endl;
        log_point_->WriteLogToDoc("SUCCESS", " 倒排序完毕", common::GetTime());
        return true;
    }
    bool init(const std::string dict_path, const std::string hmm_model_path,
        const std::string user_dict_path, const std::string idf_dict_path,
        const std::string stop_word_path, Logging* log_point_p, const std::string out_path)
    {
        log_point_ = log_point_p;
        index_.init(dict_path, hmm_model_path, user_dict_path,
            idf_dict_path, stop_word_path, log_point_p);
        return build(out_path);
    }
    // 去重
    bool IsHave(const ResultList& source, const uint64_t& id)
    {
        for(auto& tmp : source)
        {
            if(tmp.id_ == id)
                return true;
        }
        return false;
    }
    bool search(const std::string& search_key, ResultList* result)
    {
        // 把key进行分词
        std::vector<std::string> key_list;
        index_.CountWord(search_key, &key_list);
        for(std::string& key : key_list)
        {
            // 先找倒排拉链
            InvertedList inverted_list;
            if(index_.GetInvertedList(key, &inverted_list))
            {
                for(DocNode doc_node : inverted_list)
                {
                    // 这里找的话可能会重复
                    DocInfo doc;
                    if(index_.GetDocInfo(doc_node.doc_id_, &doc))
                    {
                        ResultNode node;
                        if(!IsHave(*result, doc_node.doc_id_))
                        {
                            node.id_ = doc_node.doc_id_;
                            node.url_ = doc.docUrl_;
                            node.summary_ = common::GetSummary(key, doc.docContent_);
                            if(node.summary_.length() != 0)
                            {
                                node.title_ = doc.docTitle_;
                                node.weight_ = doc_node.weight_;
                                result->push_back(node);
                            }
                        }
                        
                        
                    }
                }
            }
        }
        std::sort(result->begin(), result->end(), [](const ResultNode& a, const ResultNode& b){
            return a.weight_ > b.weight_;
        });
        return true;
    }
    std::string SearchForDoc(const std::string& key)
    {
        Json::Value root;
        ResultList result;
        int Max = 0;
        if(search(key, &result))
        {
            for(ResultNode& x : result)
            {
                if(Max == 50)
                    break;
                Json::Value item;
                item["id"] = std::to_string((long long)x.id_);
                item["title"] = x.title_;
                item["weight"] = x.weight_;
                item["url"] = x.url_;
                item["summary"] = x.summary_;
                root.append(item);
                ++Max;
            }
        }
        else
        {
            std::string tmp("fail");
            return tmp;
        }
        Json::StyledWriter writer;
        std::string tmp = writer.write(root);
        return tmp;
    }

};